* Run wikitext-escaping on plaintext sigs (no wiki markup, just name)
authorBrion Vibber <brion@users.mediawiki.org>
Tue, 15 Nov 2005 00:38:39 +0000 (00:38 +0000)
committerBrion Vibber <brion@users.mediawiki.org>
Tue, 15 Nov 2005 00:38:39 +0000 (00:38 +0000)
* Check for unbalanced HTML tags on raw sigs (markup allowed, but show
  a warning in prefs and use default sig if not balanced)

RELEASE-NOTES
includes/GlobalFunctions.php
includes/Parser.php
includes/SpecialPreferences.php
languages/Language.php

index ed6cdc2..377fd56 100644 (file)
@@ -223,6 +223,9 @@ fully support the editing toolbar, but was found to be too confusing.
 * (bug 2569) Use PATH_SEPARATOR instead of trying to guess based on
   DIRECTORY_SEPARATOR (was wrong on NetWare)
 * Require PHP 4.3.2 or higher strictly now.
+* Run wikitext-escaping on plaintext sigs (no wiki markup, just name)
+* Check for unbalanced HTML tags on raw sigs (markup allowed, but show
+  a warning in prefs and use default sig if not balanced)
 
 
 === Caveats ===
index 0c7e191..d459466 100644 (file)
@@ -1527,4 +1527,49 @@ function wfUrlProtocols() {
        return implode( '|', $x );
 }
 
+/**
+ * Check if a string is well-formed XML.
+ * Must include the surrounding tag.
+ *
+ * @param string $text
+ * @return bool
+ *
+ * @todo Error position reporting return
+ */
+function wfIsWellFormedXml( $text ) {
+       $parser = xml_parser_create( "UTF-8" );
+       
+       # case folding violates XML standard, turn it off
+       xml_parser_set_option( $parser, XML_OPTION_CASE_FOLDING, false );
+       
+       if( !xml_parse( $parser, $text, true ) ) {
+               $err = xml_error_string( xml_get_error_code( $parser ) );
+               $position = xml_get_current_byte_index( $parser );
+               //$fragment = $this->extractFragment( $html, $position );
+               //$this->mXmlError = "$err at byte $position:\n$fragment";
+               xml_parser_free( $parser );
+               return false;
+       }
+       xml_parser_free( $parser );
+       return true;
+}
+
+/**
+ * Check if a string is a well-formed XML fragment.
+ * Wraps fragment in an <html> bit and doctype, so it can be a fragment
+ * and can use HTML named entities.
+ *
+ * @param string $text
+ * @return bool
+ */
+function wfIsWellFormedXmlFragment( $text ) {
+       $html = 
+               '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ' .
+               '"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">' .
+               '<html>' .
+               $text .
+               '</html>';
+       return wfIsWellFormedXml( $html );
+}
+
 ?>
index 9bf1087..95183bd 100644 (file)
@@ -3097,30 +3097,23 @@ class Parser
 
                # Signatures
                #
-               $n = $user->getName();
-               $k = $user->getOption( 'nickname' );
-               if ( '' == $k ) { $k = $n; }
-               if ( isset( $wgLocaltimezone ) ) {
-                       $oldtz = getenv( 'TZ' );
-                       putenv( 'TZ='.$wgLocaltimezone );
-               }
+               $sigText = $this->getUserSig( $user );
 
                /* Note: This is the timestamp saved as hardcoded wikitext to
                 * the database, we use $wgContLang here in order to give
                 * everyone the same signiture and use the default one rather
                 * than the one selected in each users preferences.
                 */
+               if ( isset( $wgLocaltimezone ) ) {
+                       $oldtz = getenv( 'TZ' );
+                       putenv( 'TZ='.$wgLocaltimezone );
+               }
                $d = $wgContLang->timeanddate( date( 'YmdHis' ), false, false) .
                  ' (' . date( 'T' ) . ')';
                if ( isset( $wgLocaltimezone ) ) {
                        putenv( 'TZ='.$oldtz );
                }
 
-               if( $user->getOption( 'fancysig' ) ) {
-                       $sigText = $k;
-               } else {
-                       $sigText = '[[' . $wgContLang->getNsText( NS_USER ) . ":$n|$k]]";
-               }
                $text = preg_replace( '/~~~~~/', $d, $text );
                $text = preg_replace( '/~~~~/', "$sigText $d", $text );
                $text = preg_replace( '/~~~/', $sigText, $text );
@@ -3160,6 +3153,62 @@ class Parser
 
                return $text;
        }
+       
+       /**
+        * Fetch the user's signature text, if any, and normalize to
+        * validated, ready-to-insert wikitext.
+        *
+        * @param User $user
+        * @return string
+        * @access private
+        */
+       function getUserSig( &$user ) {
+               $name = $user->getName();
+               $nick = trim( $user->getOption( 'nickname' ) );
+               if ( '' == $nick ) {
+                       $nick = $name;
+               }
+               
+               if( $user->getOption( 'fancysig' ) ) {
+                       // A wikitext signature.
+                       $valid = $this->validateSig( $nick );
+                       if( $valid === false ) {
+                               // Fall back to default sig
+                               $nick = $name;
+                               wfDebug( "Parser::getUserSig: $name has bad XML tags in signature.\n" );
+                       } else {
+                               return $nick;
+                       }
+               }
+               
+               // Plain text linking to the user's homepage
+               global $wgContLang;
+               $page = $user->getUserPage();
+               return '[[' .
+                       $page->getPrefixedText() .
+                       "|" .
+                       wfEscapeWikIText( $nick ) .
+                       "]]";
+       }
+       
+       /**
+        * We want to enforce two rules on wikitext sigs here:
+        * 1) Expand any templates at save time (forced subst:)
+        * 2) Check for unbalanced XML tags, and reject if so.
+        *
+        * @param string $text
+        * @return mixed An expanded string, or false if invalid.
+        *
+        * @todo Run brace substitutions
+        * @todo ?? Check for unbalanced '' and ''' quotes, etc
+        */
+       function validateSig( $text ) {
+               if( wfIsWellFormedXmlFragment( $text ) ) {
+                       return $text;
+               } else {
+                       return false;
+               }
+       }
 
        /**
         * Set up some variables which are usually set up in parse()
index 77e7a2b..fd51c36 100644 (file)
@@ -463,6 +463,7 @@ class PreferencesForm {
                # <FIXME>
                $this->mUserEmail = htmlspecialchars( $this->mUserEmail );
                $this->mRealName = htmlspecialchars( $this->mRealName );
+               $rawNick = $this->mNick;
                $this->mNick = htmlspecialchars( $this->mNick );
                if ( !$this->mEmailFlag ) { $emfc = 'checked="checked"'; }
                else { $emfc = ''; }
@@ -539,11 +540,23 @@ class PreferencesForm {
                        );
                }
 
+               global $wgParser;
+               if( !empty( $this->mToggles['fancysig'] ) &&
+                       false === $wgParser->validateSig( $rawNick ) ) {
+                       $invalidSig = $this->addRow(
+                               '&nbsp;',
+                               '<span class="error">' . wfMsgHtml( 'badsig' ) . '<span>'
+                       );
+               } else {
+                       $invalidSig = '';
+               }
+               
                $wgOut->addHTML(
                        $this->addRow(
                                '<label for="wpNick">' . wfMsg( 'yournick' ) . '</label>',
                                "<input type='text' name='wpNick' id='wpNick' value=\"{$this->mNick}\" size='25' />"
                        ) .
+                       $invalidSig .
                        # FIXME: The <input> part should be where the &nbsp; is, getToggle() needs
                        # to be changed to out return its output in two parts. -ævar
                        $this->addRow(
index 8565c2a..f710c0e 100644 (file)
@@ -622,6 +622,7 @@ Your account has been created. Don't forget to change your {{SITENAME}} preferen
 'yourlanguage' => 'Language:',
 'yourvariant'  => 'Variant',
 'yournick'             => 'Nickname:',
+'badsig'               => 'Invalid raw signature; check HTML tags.',
 'email'                        => 'Email',
 'emailforlost'         => "Fields marked with superscripts are optional.  Storing an email address enables people to contact you through the website without you having to reveal your
 email address to them, and it can be used to send you a new password if you forget it.<br /><br />Your real name, if you choose to provide it, will be used for giving you attribution for your work.",